﻿using FuiouCode;
using FuiouPay.Models.Request;
using FuiouPay.Models.Response;
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

namespace FuiouPay {
    /// <summary>
    /// 支付功能
    /// </summary>
    public class Pay {
        /// <summary>
        /// 空构造函数，机构号与商户号可通过对应方法的实体传入，不传则调用测试接口
        /// </summary>
        public Pay() { }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ins_cd">机构号,接入机构在富友的唯一代码</param>
        /// <param name="mchnt_cd">商户号, 富友分配的商户号</param>
        public Pay(string ins_cd, string mchnt_cd) {
            Const.ins_cd = ins_cd;
            Const.mchnt_cd = mchnt_cd;
        }

        /// <summary>
        /// 退款
        /// </summary>
        /// <param name="refund"></param>
        /// <returns></returns>
        public RefundResponseInfo Refund(RefundRequestInfo refund) {
            if (string.IsNullOrEmpty(refund.MchntOrderNo)) {
                throw new FuiouException("mchnt_order_no不能为空");
            }

            if (string.IsNullOrEmpty(refund.OrderType)) {
                throw new FuiouException("order_type不能为空");
            }

            if (string.IsNullOrEmpty(refund.RefundOrderNo)) {
                throw new FuiouException("refund_order_no不能为空");
            }

            if (refund.TotalAmt <= 0) {
                throw new FuiouException("total_amt不能小于等于0");
            }

            if (refund.RefundAmt <= 0) {
                throw new FuiouException("refund_amt不能小于等于0");
            }

            if (refund.TotalAmt < refund.RefundAmt) {
                throw new FuiouException("refund_amt不能大于total_amt");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(refund);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            RefundResponseInfo response = Run<RefundResponseInfo>(pairs, Const.BaseUrl + Const.commonRefundUrl);

            return response;
        }

        /// <summary>
        /// 条码支付，商户扫用户二维码或条码收款
        /// </summary>
        /// <param name="micropay"></param>
        /// <returns></returns>
        public MicropayResponseInfo Micropay(MicropayRequestInfo micropay) {
            if (string.IsNullOrEmpty(micropay.OrderType)) {
                throw new FuiouException("order_type不能为空");
            }

            if (string.IsNullOrEmpty(micropay.GoodsDes)) {
                throw new FuiouException("goods_des不能为空");
            }

            if (micropay.OrderAmt <= 0) {
                throw new FuiouException("order_amt不能小于等于0");
            }

            if (string.IsNullOrEmpty(micropay.AuthCode)) {
                throw new FuiouException("auth_code不能为空");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(micropay);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            MicropayResponseInfo response = Run<MicropayResponseInfo>(pairs, Const.BaseUrl + Const.micropayUrl);

            return response;
        }

        /// <summary>
        /// 统一下单，用户扫商户二维码支付
        /// </summary>
        /// <param name="preCreate"></param>
        /// <returns></returns>
        public PreCreateResponseInfo PreCreate(PreCreateRequestInfo preCreate) {
            if (string.IsNullOrEmpty(preCreate.OrderType)) {
                throw new FuiouException("order_type不能为空");
            }

            if (string.IsNullOrEmpty(preCreate.GoodsDes)) {
                throw new FuiouException("goods_des不能为空");
            }

            if (preCreate.OrderAmt <= 0) {
                throw new FuiouException("order_amt不能小于等于0");
            }

            if (string.IsNullOrEmpty(preCreate.TxnBeginTs)) {
                throw new FuiouException("txn_begin_ts不能为空");
            }

            if (string.IsNullOrEmpty(preCreate.NotifyUrl)) {
                throw new FuiouException("notify_ur不能为空");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(preCreate);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            PreCreateResponseInfo response = Run<PreCreateResponseInfo>(pairs, Const.BaseUrl + Const.preCreateUrl);

            return response;
        }

        /// <summary>
        /// 公众号/服务窗统一下单
        /// </summary>
        /// <param name="wxPreCreate"></param>
        /// <returns></returns>
        public WxPreCreateResponseInfo WxPreCreate(WxPreCreateRequestInfo wxPreCreate) {
            if (string.IsNullOrEmpty(wxPreCreate.GoodsDes)) {
                throw new FuiouException("goods_des不能为空");
            }

            if (wxPreCreate.OrderAmt <= 0) {
                throw new FuiouException("order_amt不能小于等于0");
            }

            if (string.IsNullOrEmpty(wxPreCreate.TxnBeginTs)) {
                throw new FuiouException("txn_begin_ts不能为空");
            }

            if (string.IsNullOrEmpty(wxPreCreate.NotifyUrl)) {
                throw new FuiouException("notify_ur不能为空");
            }

            if (string.IsNullOrEmpty(wxPreCreate.TradeType)) {
                throw new FuiouException("trade_type不能为空");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(wxPreCreate);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            WxPreCreateResponseInfo response = Run<WxPreCreateResponseInfo>(pairs, Const.BaseUrl + Const.wxPreCreateUrl);

            return response;
        }

        /// <summary>
        /// 订单查询
        /// </summary>
        /// <param name="commonQuery"></param>
        /// <returns></returns>
        public CommonQueryResponseInfo CommonQuery(CommonQueryRequestInfo commonQuery) {
            if (string.IsNullOrEmpty(commonQuery.MchntOrderNo)) {
                throw new FuiouException("mchnt_order_no不能为空");
            }

            if (string.IsNullOrEmpty(commonQuery.OrderType)) {
                throw new FuiouException("order_type不能为空");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(commonQuery);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            CommonQueryResponseInfo response = Run<CommonQueryResponseInfo>(pairs, Const.BaseUrl + Const.commonQueryUrl);

            return response;
        }

        /// <summary>
        /// 关闭订单
        /// </summary>
        /// <param name="closeOrder"></param>
        /// <returns></returns>
        public CloseOrderResponseInfo CloseOrder(CloseOrderRequestInfo closeOrder) {
            if (string.IsNullOrEmpty(closeOrder.MchntOrderNo)) {
                throw new FuiouException("mchnt_order_no不能为空");
            }

            if (string.IsNullOrEmpty(closeOrder.OrderType)) {
                throw new FuiouException("order_type不能为空");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(closeOrder);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            CloseOrderResponseInfo response = Run<CloseOrderResponseInfo>(pairs, Const.BaseUrl + Const.closeorderUrl);

            return response;
        }

        /// <summary>
        /// 撤消订单
        /// </summary>
        /// <param name="cancelOrder"></param>
        /// <returns></returns>
        public CancelOrderResponseInfo CancelOrder(CancelOrderRequestInfo cancelOrder) {
            if (string.IsNullOrEmpty(cancelOrder.MchntOrderNo)) {
                throw new FuiouException("mchnt_order_no不能为空");
            }

            if (string.IsNullOrEmpty(cancelOrder.OrderType)) {
                throw new FuiouException("order_type不能为空");
            }

            if (string.IsNullOrEmpty(cancelOrder.CancelOrderNo)) {
                throw new FuiouException("cancel_order_no不能为空");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(cancelOrder);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            CancelOrderResponseInfo response = Run<CancelOrderResponseInfo>(pairs, Const.BaseUrl + Const.cancelorderUrl);

            return response;
        }

        /// <summary>
        /// 退款查询
        /// </summary>
        /// <param name="refundQuery"></param>
        /// <returns></returns>
        public RefundQueryResponseInfo RefundQuery(RefundQueryRequestInfo refundQuery) {
            if (string.IsNullOrEmpty(refundQuery.RefundOrderNo)) {
                throw new FuiouException("refund_order_no不能为空");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(refundQuery);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            RefundQueryResponseInfo response = Run<RefundQueryResponseInfo>(pairs, Const.BaseUrl + Const.refundQueryUrl);

            return response;
        }

        /// <summary>
        ///  历史订单查询
        /// </summary>
        /// <param name="hisTradeQuery"></param>
        /// <returns></returns>
        public HisTradeQueryResponseInfo HisTradeQuery(HisTradeQueryRequestInfo hisTradeQuery) {
            if (string.IsNullOrEmpty(hisTradeQuery.MchntOrderNo) && string.IsNullOrEmpty(hisTradeQuery.ChannelOrderId) && string.IsNullOrEmpty(hisTradeQuery.TransactionId)) {
                throw new FuiouException("mchnt_order_no、channel_order_id、transaction_id三选一");
            }

            if (string.IsNullOrEmpty(hisTradeQuery.OrderType)) {
                throw new FuiouException("order_type不能为空");
            }

            Dictionary<string, object> pairs = Utils.ObjectToMap(hisTradeQuery);

            if (!pairs.IsSet("ins_cd")) {
                pairs.SetValue("ins_cd", Const.ins_cd);
            }

            if (!pairs.IsSet("mchnt_cd")) {
                pairs.SetValue("mchnt_cd", Const.mchnt_cd);
            }

            if (!pairs.IsSet("term_id")) {
                pairs.SetValue("term_id", Const.term_id);
            }

            if (!pairs.IsSet("random_str")) {
                pairs.SetValue("random_str", Guid.NewGuid().ToString("N"));
            }

            HisTradeQueryResponseInfo response = Run<HisTradeQueryResponseInfo>(pairs, Const.BaseUrl + Const.hisTradeQueryUrl);

            return response;
        }

        /// <summary>
        /// 通知结果验签并返回实体
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public NotifyResponseInfo Notify(string value) {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            value = HttpUtility.UrlDecode(value, Encoding.GetEncoding(Const.charset));

            Console.WriteLine("urldecode后的字符串：" + value);

            if (Utils.CheckSign(value, Const.privateKey, Const.charset)) {
                return Utils.XmlDeserialize<NotifyResponseInfo>(value, Const.charset);
            }

            return default;
        }

        private static T Run<T>(Dictionary<string, object> map, string url) where T : class {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            Encoding encoding = Encoding.GetEncoding(Const.charset);

            Dictionary<string, object> nvs = new Dictionary<string, object>();

            string reqBody = "<?xml version=\"1.0\" encoding=\"" + Const.charset + "\" standalone=\"yes\"?>" + map.MapToXml(Const.privateKey, Const.charset);

            Console.WriteLine("==============================待编码字符串==============================\r\n" + reqBody);

            reqBody = Utils.UrlEncode(reqBody, encoding);

            Console.WriteLine("==============================编码后字符串==============================\r\n" + reqBody);

            nvs.Add("req", reqBody);

            StringBuilder result = new StringBuilder("");

            HttpUtils httpUtils = new HttpUtils();
            httpUtils.Post(url, nvs, result, Const.charset);

            Console.WriteLine("==============================输出字符串==============================\r\n" + HttpUtility.UrlDecode(result.ToString(), encoding));

            string strResult = HttpUtility.UrlDecode(result.ToString(), encoding);

            if (Utils.CheckSign(strResult, Const.publicKey, Const.charset)) {
                return Utils.XmlDeserialize<T>(strResult, Const.charset);
            } else {
                throw new FuiouException("返回数据异常，或验签失败");
            }
        }
    }
}
